昨天我們利用 gin
做了一個簡單的網頁,但網站可不會只有看的功能,有時候使用者也需要輸入資料與網站進行互動,因此,今天我們會說明如何利用 gin 實作一個 登入
的功能。
首先先來訂定要完成的目標,今天預期要完成幾件事情
登入
的核心程式/login
路徑進行綁定登入成功
的訊息,登入失敗會顯示 帳號或密碼錯誤
訊息訂定好目標後,就讓我們開始吧!
在開始寫程式之前,我們先思考一下要實作登入功能的流程,以下為一個簡單的登入流程
有了以上架構後就可以開始進行程式開發拉
首先,我們先將使用者的帳號密碼定義為 map
,並且在 init
的方法中進行初始化
如果想要加入更多測試的帳號密碼,可以在 map
中依序加入
var UserData map[string]string
func init() {
UserData = map[string]string{
"test": "test",
}
}
接著我們試著寫出判斷使用者是否存在的程式,邏輯就是為判斷 map
中是否擁有匹配的 key
[備註]本範例因為沒有串連資料庫,因此就是單純使用變數來做判斷
func CheckUserIsExist(username string) bool {
_, isExist := UserData[username]
return isExist
}
再來寫出密碼比對
func CheckPassword(p1 string, p2 string) error {
if p1 == p2 {
return nil
} else {
return errors.New("password is not correct")
}
}
然後再將上面的程式拼湊成驗證身份的程式
func Auth(username string, password string) error {
if isExist := CheckUserIsExist(username); isExist {
return CheckPassword(UserData[username], password)
} else {
return errors.New("user is not exist")
}
}
最後將所有的程式放入名為 auth.go
的檔案中並放置於根目錄,內容如下
package main
import "errors"
var UserData map[string]string
func init() {
UserData = map[string]string{
"test": "test",
}
}
func CheckUserIsExist(username string) bool {
_, isExist := UserData[username]
return isExist
}
func CheckPassword(p1 string, p2 string) error {
if p1 == p2 {
return nil
} else {
return errors.New("password is not correct")
}
}
func Auth(username string, password string) error {
if isExist := CheckUserIsExist(username); isExist {
return CheckPassword(UserData[username], password)
} else {
return errors.New("user is not exist")
}
}
以上就完成了核心程式了
有了核心邏輯的程式後,我們就來製作畫面吧
首先,先建立好登入的 request handler,這邊我們建立兩個,一個為接收 GET
一個接收 POST
func LoginPage(c *gin.Context) {
}
func LoginAuth(c *gin.Context) {
}
LoginPage 基本上只要顯示出登入的畫面即可,因此我們先建立好的登入的 html,登入的 html 如下
<link href="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel="stylesheet" id="bootstrap-css">
<link href="/assets/css/custom.css" rel="stylesheet">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
{{ if .success }}
<div class="alert alert-success" role="alert">
{{ .success }}
</div>
{{ end }}
{{if .error}}
<div class="alert alert-danger" role="alert">
{{ .error }}
</div>
{{end}}
<div class="container register-form">
<form METHOD="POST" ACTION="/login">
<div class="form">
<div class="note">
<p>登入範例</p>
</div>
<div class="form-content">
<div class="row">
<div class="col-md-12">
<div class="form-group">
<input type="text" class="form-control" placeholder="使用者名稱" name="username" value=""/>
</div>
<div class="form-group">
<input type="password" class="form-control" placeholder="密碼" name="password" value=""/>
</div>
</div>
</div>
<button type="submit" class="btnSubmit">登入</button>
</div>
</div>
</form>
</div>
CSS 如下
.note
{
text-align: center;
height: 80px;
background: -webkit-linear-gradient(left, #0072ff, #8811c5);
color: #fff;
font-weight: bold;
line-height: 80px;
}
.form-content
{
padding: 5%;
border: 1px solid #ced4da;
margin-bottom: 2%;
}
.form-control{
border-radius:1.5rem;
}
.btnSubmit
{
border:none;
border-radius:1.5rem;
padding: 1%;
width: 20%;
cursor: pointer;
background: #0062cc;
color: #fff;
}
我們將檔案分別處理
html
儲存成 login.html
後,放置到 ./template/html
內css
儲存成 custom.css
後,放置到 ./template/assets/css
內[備註] 在 html 中的 if
是用於如果我們有這個變數存在了才為顯示裡面所放的元素
接著我們修改 LoginPage
,將其改成下面這樣
func LoginPage(c *gin.Context) {
c.HTML(http.StatusOK, "login.html", nil)
}
將剛才寫好的 login.html
透過 LoginPage
進行渲染
最後,我們在 main.go
裡面將 GET /login
與 LoginPage
進行綁定
server.GET("/login", LoginPage)
完成後的 main.go
會像這樣
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
server := gin.Default()
server.LoadHTMLGlob("template/html/*")
//設定靜態資源的讀取
server.Static("/assets", "./template/assets")
server.GET("/login", LoginPage)
server.Run(":8888")
}
將程式運行起來後,輸入 http://127.0.0.1:8888/login
看到以下畫面就代表成功了
設定完登入畫面後,我們開始寫登入功能
首先我們先設定好會用到的變數,因為這次主要會用到的就是 帳號
與 密碼
,因此我們設定如下
var (
username string
password string
)
再來我們設定 POST FORM 的接收,gin.Context
提供了 GetPostForm
方法可以使用,因此我們判斷是否有輸入帳號密碼的程式如下
if in, isExist := c.GetPostForm("username"); isExist && in != "" {
username = in
} else {
c.HTML(http.StatusBadRequest, "login.html", gin.H{
"error": errors.New("必須輸入使用者名稱"),
})
return
}
if in, isExist := c.GetPostForm("password"); isExist && in != "" {
password = in
} else {
c.HTML(http.StatusBadRequest, "login.html", gin.H{
"error": errors.New("必須輸入密碼名稱"),
})
return
}
如果沒有輸入帳號或是密碼,就會回傳 error
,在畫面上就會顯示出錯誤了
接著判斷輸入的帳號密碼是否有誤,可以很簡單的呼叫最上面我們封裝的 Auth
方法即可,程式如下
if err := Auth(username, password); err == nil {
c.HTML(http.StatusOK, "login.html", gin.H{
"success": "登入成功",
})
return
} else {
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
"error": err,
})
return
}
最後拼湊出來的程式如下
func LoginAuth(c *gin.Context) {
var (
username string
password string
)
if in, isExist := c.GetPostForm("username"); isExist && in != "" {
username = in
} else {
c.HTML(http.StatusBadRequest, "login.html", gin.H{
"error": errors.New("必須輸入使用者名稱"),
})
return
}
if in, isExist := c.GetPostForm("password"); isExist && in != "" {
password = in
} else {
c.HTML(http.StatusBadRequest, "login.html", gin.H{
"error": errors.New("必須輸入密碼名稱"),
})
return
}
if err := Auth(username, password); err == nil {
c.HTML(http.StatusOK, "login.html", gin.H{
"success": "登入成功",
})
return
} else {
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
"error": err,
})
return
}
}
最後,我們在 main.go
裡面將 GET /login
與 LoginAuth
進行綁定
server.POST("/login", LoginPage)
完成後的 main.go
會像這樣
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
server := gin.Default()
server.LoadHTMLGlob("template/html/*")
//設定靜態資源的讀取
server.Static("/assets", "./template/assets")
server.GET("/login", LoginPage)
server.POST("/login", LoginAuth)
server.Run(":8888")
}
最後,我們可以來測試功能拉,設定好幾個情境進行測試,如果看到以下畫面代表正確
今天的範例很簡單的實作了最常見的使用者登入的功能,但登入的資訊如果要增加每一次都要修改程式好麻煩的,通常這個時候我們就會利用 資料庫
來解決這個問題!
因此明天就來學習怎麼透過 orm
來存取資料庫吧!
由於週末兩天都特別忙碌,今天這篇算是趕著出來的,有任何的錯誤再麻煩大家告知了~
這次沒有任何的參考資料~
我應該把LoginAuth放在哪個程式,
我目前放在main.go
但執行他讀不到auth
執行全部試試看
還是一樣耶⋯⋯
LoginAuth 是放在 main.go
裡面哦,基本上不管切成幾個檔案,在同一個目錄底下 package 名稱相同就不需要額外的做 import
我執行的時候也讀不到Auth
將我的問題與解決方式留下
問題:panic: html/template: pattern matches no files:
解決:如果建立的檔案,不是在GO的安裝目錄底下
將func main裡面的
server.LoadHTMLGlob("template/html/")
改成server.LoadHTMLGlob("./template/html/")
原因:go的環境變數預設為安裝目錄,直接使用相對路徑會找不到,要加上"./"
參考:來源